-
Notifications
You must be signed in to change notification settings - Fork 387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add user season score calculation workflow #11768
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mostly commenting on the db structure, I didn't review all the code too closely
return $this->scoreFactors() | ||
->orderByDesc('factor') | ||
->get() | ||
->pluck('factor') | ||
->toArray(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems factors will only be useful for this exact query (get all factors for a season), and don't relate to any models besides seasons, so I think you can just store the factors in a column of Season
with array cast. no need to cache either
|
||
/** | ||
* @property int $id | ||
* @property float $factor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also want to question whether the concept of "factors" here is important to the score calculation because it seems a little complicated to me (as a player) and I can't imagine what would cause you to tune the factors individually. would something more straightforward like how total pp is weighted not work?
* @property float $total_score | ||
* @property int $user_id | ||
*/ | ||
class UserSeasonScore extends Model |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
staying consistent with existing naming conventions i think this should be UserSeasonScoreAggregate
$factors = $this->season->scoreFactorsOrderedForCalculation(); | ||
$parentRooms = $this->season->rooms->where('parent_id', null); | ||
|
||
if ($parentRooms->count() > count($factors)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like these have to be equal?
$parentRooms = $this->season->rooms->where('parent_id', null); | ||
|
||
if ($parentRooms->count() > count($factors)) { | ||
throw new InvariantException(osu_trans('rankings.seasons.validation.not_enough_factors')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any usage of calculate()
besides the recalc command should handle this by skipping the update to user's season score, for example an invalid factors config shouldn't be throwing away the rest of Room::completePlay()
, you can always recalculate season scores later if it fails for whatever reason
@@ -37,6 +37,7 @@ | |||
* @property int $id | |||
* @property int|null $max_attempts | |||
* @property string $name | |||
* @property int|null $parent_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be in SeasonRoom
table instead. I also think it may be easier to manage/implement by storing the playlist letter itself, instead of linking rooms together
$childRoomId = $this->season->rooms | ||
->where('parent_id', $room->getKey()) | ||
->first() | ||
?->getKey(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should allow more than one child so you can re-run playlists more than once. well idk if that would ever actually happen but there's no reason to hardcode this in either (same with unique constraint on parent ID)
Part of #8736.
Serving as a base for implementation of seasonal rankings in nearing feature.
This mimics the Beatmap Spotlights seasonal rankings calculation process that used to be done via an external script.
Also adds an artisan command for score recalculating.
Tried to optimize the calculation process as much as possible, any further potential improvements on that matter would be vastly appreciated. 🤔
How does this work?
Rooms
The seasons are designed as a series of few (currently: three) playlists that are usually labeled by consecutive letters of alphabet (A, B, C).
Each of the playlists is ran twice over a course of the season. For the sake of this implementation, the rooms of the first run of the playlists are referred to as "parent rooms", while the rooms of the second run are called "children rooms".
This PR adds
parent_id
field to themultiplayer_rooms
table, to be used on children rooms.Score calculation
Total (user) season score is sum of best playlist total scores achieved throughout the season, with added factor-based weighting.
Total scores of a each playlist are compared between the parent and child rooms, with higher being taken for total season score calculation.
Said scores are sorted from highest to lowest, and season-specified factors are being applied in descending order to each of them (e.g. 1.0, 0.75, 0.5). This results in final total season score.
This PR adds the following:
user_season_scores
table,total_score
of which is calculated using related modelcalculate()
function, that is called whenever user places a score on related season's room.season_score_factors
table, that stores factors, used for total season score calculation.Example user total season score calculation
Let's say a user achieves following scores during the six playlists of the season:
The season score factors are set to following: 1, 0.75, 0.5.
Playlist final scores are being sorted in descending order, resulting in following values: 40, 20, 12. The final season score calculation goes as below:
The total season score is 61. That value would be used for sake of placing user in seasonal leaderboard.